
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */ 
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

#include "Scene.h"
#include "GeoSphere.h"
#include "GeoBox.h"
#include "GeoCylinder.h"
#include "GeoCone.h"
#include "GeoTorus.h"
#include "GeoVertex.h"
#include "GeoMesh.h"

#include "DirectionalLight.h"
#include "DirectionalAt.h"
#include "PointLight.h"
#include "SpotLight.h"
#include "SpotAt.h"

#include "CameraEye.h"
#include "CameraAt.h"
#include "CameraUp.h"
#include "OGLVisu.h"

#include <sstream>
using std::stringstream;

//initialise the singleton pointer
Scene* Scene::_instance = 0;

//constructor
Scene::Scene():_selectedObject(0), _primitives(), _lights(), _primitiveCount(0), _firstSelectedObject(0)
{
	//create the camera
	_cameraAt = new CameraAt();
	_cameraUp = new CameraUp();
	_cameraEye = new CameraEye(_cameraUp, _cameraAt);
	_globalAmbientLight[0] = 0.2f;
	_globalAmbientLight[1] = 0.2f;
	_globalAmbientLight[2] = 0.2f;
	_globalAmbientLight[3] = 0.2f;
}

//destructor
Scene::~Scene()
{
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		delete (*i);
	}

	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		delete (*i);
	}

	delete _cameraEye;
	delete _cameraAt;
	delete _cameraUp;
	_cameraEye = 0;
	_cameraAt = 0;
	_cameraUp = 0;
}

//save a mesh
bool Scene::saveMesh(ofstream& ofs, GeoMesh* m)
{
	//save material
	ofs.write((char*)m->getMaterial().getAmbient(), 4*sizeof(float));
	ofs.write((char*)m->getMaterial().getDiffuse(), 4*sizeof(float));
	ofs.write((char*)m->getMaterial().getSpecular(), 4*sizeof(float));
	int shininess = m->getMaterial().getShininess();
	ofs.write((char*)&shininess, sizeof(float));
	string filename = m->getMaterial().getTextureFilename();
	int length = filename.length();
	ofs.write((char*)&length, sizeof(int));
	const char* ptrFilename = filename.c_str();
	ofs.write(ptrFilename, length*sizeof(char));

	//save transform T, R, S
	ofs.write((char*)&m->getTranslate(), sizeof(CVector3f));
	ofs.write((char*)&m->getRotate(), sizeof(CVector3f));
	ofs.write((char*)&m->getScale(), sizeof(CVector3f));

	//save vertex buffer
	int vertexCount = m->_vertexBuffer.size();
	ofs.write((char*)&vertexCount, sizeof(int));
	for(vector<meshVertex>::iterator i = m->_vertexBuffer.begin(); i != m->_vertexBuffer.end(); i++)
		ofs.write((char*)&(*i), sizeof(meshVertex));

	//save index buffer
	int indexCount = m->_indexBuffer.size();
	ofs.write((char*)&indexCount, sizeof(int));
	for(vector<int>::iterator i = m->_indexBuffer.begin(); i != m->_indexBuffer.end(); i++)
		ofs.write((char*)&(*i), sizeof(int));

	//save normal per triangle buffer
	int normalCount = m->_normalPerTriangleBuffer.size();
	ofs.write((char*)&normalCount, sizeof(int));
	for(vector<CVector3f>::iterator i = m->_normalPerTriangleBuffer.begin(); i != m->_normalPerTriangleBuffer.end(); i++)
		ofs.write((char*)&(*i), sizeof(CVector3f));

	//save uv map buffer
	int uvCount = m->_uvMapBuffer.size();
	ofs.write((char*)&uvCount, sizeof(int));
	for(vector<CVector3f>::iterator i = m->_uvMapBuffer.begin(); i != m->_uvMapBuffer.end(); i++)
		ofs.write((char*)&(*i), sizeof(CVector3f));

	return true;

}

//load a mesh
bool Scene::loadMesh(ifstream& ifs, GeoMesh* m)
{
	//load the material
	float material[4];
	ifs.read((char*)material, 4*sizeof(float));
	m->getMaterial().setAmbient(material[0], material[1], material[2], material[3]);
	ifs.read((char*)material, 4*sizeof(float));
	m->getMaterial().setDiffuse(material[0], material[1], material[2], material[3]);
	ifs.read((char*)material, 4*sizeof(float));
	m->getMaterial().setSpecular(material[0], material[1], material[2], material[3]);
	int shininess = 0;
	ifs.read((char*)&shininess, sizeof(int));
	m->getMaterial().setShininess(shininess);
	int length = 0;
	ifs.read((char*)&length, sizeof(int));
	char* ptrFilename = new char[length+1];
	ifs.read(ptrFilename, length*sizeof(char));
	ptrFilename[length] = '\0';
	m->getMaterial().setTextureName(ptrFilename);
	delete ptrFilename;

	m->getMaterial().setTexture();

	//load TRS
	CVector3f temp;
	ifs.read((char*)&temp, sizeof(CVector3f));
	m->setTranslate(temp);
	ifs.read((char*)&temp, sizeof(CVector3f));
	m->setRotation(temp);
	ifs.read((char*)&temp, sizeof(CVector3f));
	m->setScale(temp);

	//load vertex buffer
	int vertexCount = 0;
	ifs.read((char*)&vertexCount, sizeof(int));
	for(int i = 0; i < vertexCount; i++)
	{
		meshVertex newVertex(CVector3f(0, 0, 0), CVector3f(0, 0, 0));
		ifs.read((char*)&newVertex, sizeof(meshVertex));
		m->_vertexBuffer.push_back(newVertex);
	}

	//load index buffer
	int indexCount = 0;
	ifs.read((char*)&indexCount, sizeof(int));
	for(int i = 0; i < indexCount; i++)
	{
		int newIndex = 0;
		ifs.read((char*)&newIndex, sizeof(int));
		m->_indexBuffer.push_back(newIndex);
	}

	//load normal buffer
	int normalCount = 0;
	ifs.read((char*)&normalCount, sizeof(int));
	for(int i = 0; i < normalCount; i++)
	{
		CVector3f newNormal;
		ifs.read((char*)&newNormal, sizeof(CVector3f));
		m->_normalPerTriangleBuffer.push_back(newNormal);
	}

	//load uv map buffer
	int uvCount = 0;
	ifs.read((char*)&uvCount, sizeof(int));
	for(int i = 0; i < uvCount; i++)
	{
		CVector3f newUV;
		ifs.read((char*)&newUV, sizeof(CVector3f));
		m->_uvMapBuffer.push_back(newUV);
	}

	//calculate the bounding box
	m->calculateAABB();

	//create the geovertex for this mesh
	m->createAllGeoVertex();

	return true;
}

//return the singleton
Scene& Scene::getInstance()
{
	if (_instance == 0)
		_instance = new Scene();

	return *_instance;
}

//destroy the singleton
void Scene::close()
{
	if (_instance == 0)
	{
		delete _instance;
		_instance = 0;
	}
}

//Create sphere and add it to the scene. Return a pointer to the sphere object.
GeoSphere* Scene::createSphere()
{
	//create the new sphere
	GeoSphere* newGeo = new GeoSphere();

	//add the sphere to the scene
	_primitives << newGeo;

	//return the pointer
	return newGeo;
}

//Create box and add it to the scene. Return a pointer to the sphere object.
GeoBox* Scene::createBox()
{
	//create the new box
	GeoBox* newBox = new GeoBox();

	//add the primitive to the scene
	_primitives << newBox;

	//return the pointer
	return newBox;
}

//Create a cylinder and add it to the scene. Return a pointer to the sphere object.
GeoCylinder* Scene::createCylinder()
{
	//create the new box
	GeoCylinder* newCylinder = new GeoCylinder();

	//add the primitive to the scene
	_primitives << newCylinder;

	//return the pointer
	return newCylinder;
}

//Create a cone and add it to the scene. Return a pointer to the sphere object.
GeoCone* Scene::createCone()
{
	//create the new cone
	GeoCone* newCone = new GeoCone();

	//add the primitive to the scene
	_primitives << newCone;

	//return the pointer
	return newCone;
}

//Create a torus and add it to the scene. Return a pointer to the sphere object.
GeoTorus* Scene::createTorus()
{
	//create the new torus
	GeoTorus* newTorus = new GeoTorus();

	//add the primitive to the scene
	_primitives << newTorus;

	//return the pointer
	return newTorus;
}

//Create a vertex and add it to the scene. Return a pointer to the vertex object
GeoVertex* Scene::createVertex()
{
	//create the new vertex
	GeoVertex* newV = new GeoVertex();

	//add the primitive to the scene
	//_primitives << newV;
	_primitives.push_front(newV);
	//return the pointer
	return newV;
}

//Create a mesh and add it to the scene. Return a pointer to the vertex object.
GeoMesh* Scene::createMesh()
{
	//create the mesh
	GeoMesh* newMesh = new GeoMesh();

	//add to the scene
	_primitives << newMesh;

	//return the pointer
	return newMesh;
}

//Create a directionnal light and return a pointer to it.
DirectionalLight* Scene::createDirectionalLight()
{
	//create the at element
	DirectionalAt* atElement = new DirectionalAt();
	_lights << atElement;

	//create the light
	DirectionalLight* newLight = new DirectionalLight(atElement);

	//add the light to the scene
	_lights << newLight;

	//return the pointer
	return newLight;
}

//Create a point light and return a pointer to it.
PointLight* Scene::createPointLight()
{
	//create the light
	PointLight* newLight = new PointLight();

	//add the light to the scene
	_lights << newLight;

	//return the pointer
	return newLight;
}

//Create a spot lighht and return a pointer to it.
SpotLight* Scene::createSpotLight()
{
	//create the at element
	SpotAt* atElement = new SpotAt();
	_lights << atElement;

	//create the light
	SpotLight* newLight = new SpotLight(atElement);

	//add the light to the scene
	_lights << newLight;

	//return the pointer
	return newLight;
}

//Find the primitives corresponding to the point.
//Return 0 if no primitives is found.
Movable* Scene::getMovable(const CVector3f& v)
{
	//test light
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		if((*i)->isGeometry(v))
			return (*i);
	}
	
	//test camera
	if(_cameraEye->isGeometry(v))
		return _cameraEye;
	if(_cameraAt->isGeometry(v))
		return _cameraAt;
	if(_cameraUp->isGeometry(v))
		return _cameraUp;

	//test geometry : get a list of maximum two selected geometry
	//QLinkedList<Geometry*> selectedList;
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		if((*i)->isGeometry(v))
			return (*i);
	}

	//no intersection
	return 0;
}

//Draw the entire scene
void Scene::drawScene()
{
	_lightID = 0;

	VISU.addGlobalAmbientLight(_globalAmbientLight);

	//draw the primitives
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		(*i)->draw();
	}

	//draw the lights
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		(*i)->draw();
	}

	//draw the camera
	_cameraEye->draw();
	_cameraAt->draw();
	_cameraUp->draw();
}

//draw scene as render
void Scene::drawSceneAsRender()
{
	_lightID = 0;

	//draw the primitives
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		(*i)->drawAsRender();
	}

	//draw the lights
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		(*i)->drawAsRender();
	}
}

void Scene::setSelectedObject(Movable* g)
{
	//unselecte the object
	unselectObject();

	//select the new object
	_selectedObject = g;
	g->setSelected(true);
}

void Scene::unselectObject()
{
	if(_selectedObject == 0)
		return;

	if(_selectedObject->getMovableType() == MT_GEOMETRY)
	{
		if(((Geometry*)_selectedObject)->getType() == GT_VERTEX)
		{
			GeoVertex* v = (GeoVertex*)_selectedObject;
			GeoMesh* m = v->getMesh();
			m->setDrawSpecialTriangle(-1, -1);
		}
	}
	_selectedObject->setSelected(false);
	_selectedObject = 0;
}

//set the camera position
void Scene::setCameraEye(const CVector3f& e)
{
	_eye = e;
}

//return the camera position
CVector3f Scene::getCameraEye()const
{
	return _eye;
}

Movable* Scene::getSelectedObject()const
{
	return _selectedObject;
}

//create a new unique name for a primitive
void Scene::setPrimitiveNewName(string& n)
{
	//convert the count to string
	stringstream ss;
	ss<<_primitiveCount;

	//set the name
	n = "primitive";
	n.append(ss.str());

	//increase the count
	_primitiveCount++;
}

//delete a primitive
void Scene::deletePrimitive(Geometry* g)
{
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		if((*i) == g)
		{
			delete (*i);
			_primitives.erase(i);
			return;
		}
	}
}

//delete a light
void Scene::deleteLight(GUIILight* l)
{
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		if((*i) == l)
		{
			delete (*i);
			_lights.erase(i);
			return;
		}
	}
}

//delete the selected object. Return true if the object has been deleted.
bool Scene::deleteSelectedObject()
{
	//nothing selected so nothing to delete
	if(_selectedObject == 0)
		return false;

	GUIILight* light = 0;
	DirectionalLight* dirLight = 0;
	SpotLight* spotLight = 0;
	Geometry* geoPrimitive = 0;
	//delete the selected primitive
	switch(_selectedObject->getMovableType())
	{
		case MT_GEOMETRY:
			geoPrimitive = (Geometry*)_selectedObject;

			//if vertex, delete also the vertex in the mesh
			if (geoPrimitive->getType() == GT_VERTEX)
				((GeoVertex*)geoPrimitive)->deleteVertexInMesh();

			//if mesh, delete also the vertices
			if (geoPrimitive->getType() == GT_MESH)
				((GeoMesh*)geoPrimitive)->deleteAllGeoVertex();

			deletePrimitive(geoPrimitive);
			_selectedObject = 0;
			break;
		
		case MT_LIGHT:
			//convert the pointer to a light
			light = (GUIILight*) _selectedObject;

			//if the light is an at element => exit
			if(light->getType() == LT_DIRECTIONAL_AT || light->getType() == LT_SPOT_AT)
				return false;

			//id the light is directional => delete the at element
			if(light->getType() == LT_DIRECTIONAL)
			{
				dirLight = (DirectionalLight*)light;
				deleteLight(dirLight->getAtElement());
			}

			//if the light is a spot light => delete the at element
			if(light->getType() == LT_SPOT)
			{
				spotLight = (SpotLight*)light;
				deleteLight(spotLight->getAtElement());
			}

			//delete the light
			deleteLight((GUIILight*) _selectedObject);
			_selectedObject = 0;
			break;
		default:
			break;
	}
	
	return true;
}

//get the current light ID max
int Scene::getLightID()const
{
	return _lightID;
}

	//set tje light id
void Scene::setLightID(int lightID)
{
	_lightID = lightID;
}

//get the camera object
CameraEye* Scene::getCamera()const
{
	return _cameraEye;
}

//set the global ambient light
void Scene::setGlobalAmbientLight(float r, float g, float b, float a)
{
	_globalAmbientLight[0] = r;
	_globalAmbientLight[1] = g;
	_globalAmbientLight[2] = b;
	_globalAmbientLight[3] = a;
}

//get the global ambient light of the scene
float* Scene::getGlobalAmbientLight()
{
	return _globalAmbientLight;
}

//save the scene into a file
bool Scene::saveScene(const string& filename)
{
	if (filename == "")
		return false;

	//open an output file stream
	ofstream ofs;//(filename.c_str());
	ofs.open(filename.c_str());

	//write file version
	int versionNumber = 1;
	ofs.write((char*)&versionNumber, sizeof(int));

	//write number of primitive
	int primitiveCount = 0;
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
		if((*i)->getType() != GT_VERTEX)
			primitiveCount++;

	//_primitives.count();
	ofs.write((char*)&primitiveCount, sizeof(int));

	//write the number of light
	int lightCount = 0;//_lights.count();
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		LIGHTTYPE type = (*i)->getType();
		if (type != LT_SPOT_AT && type != LT_DIRECTIONAL_AT)
			lightCount ++;
	}
	ofs.write((char*)&lightCount, sizeof(int));

	//write the primitives
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		
		//write the primitive type
		GEOMETRYTYPE type = (*i)->getType();

		//don't save vertex
		if (type == GT_VERTEX)continue;

		ofs.write((char*)&type, sizeof(GEOMETRYTYPE));

		//write the primitive
		switch(type)
		{
			case GT_SPHERE:
				ofs.write((char*)*i, sizeof(GeoSphere));
				break;
			case GT_BOX:
				ofs.write((char*)*i, sizeof(GeoBox));
				break;
			case GT_CYLINDER:
				ofs.write((char*)*i, sizeof(GeoCylinder));
				break;
			case GT_TORUS:
				ofs.write((char*)*i, sizeof(GeoTorus));
				break;
			case GT_MESH:
				saveMesh(ofs, (GeoMesh*)*i);
				break;
			default:
				return false;
				//continue;
		}
	}

	//write lights
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		LIGHTTYPE type = (*i)->getType();
		DirectionalLight* dir = 0;
		SpotLight* spot = 0;
		switch(type)
		{
			case LT_DIRECTIONAL:
				//write the light type
				ofs.write((char*)&type, sizeof(LIGHTTYPE));
				ofs.write((char*)*i, sizeof(DirectionalLight));

				//write the at
				dir = (DirectionalLight*)(*i);
				ofs.write((char*)dir->getAtElement(), sizeof(DirectionalAt));
				break;
			case LT_SPOT:
				//write the light type
				ofs.write((char*)&type, sizeof(LIGHTTYPE));
				ofs.write((char*)*i, sizeof(SpotLight));

				//write the at
				spot = (SpotLight*)(*i);
				ofs.write((char*)spot->getAtElement(), sizeof(SpotAt));
				break;
			case LT_POINT:
				//write the light type
				ofs.write((char*)&type, sizeof(LIGHTTYPE));
				ofs.write((char*)*i, sizeof(PointLight));
				break;
			default:
				break;
		}
	}

	//write camera info
	ofs.write((char*)_cameraEye, sizeof(CameraEye));
	ofs.write((char*)_cameraAt, sizeof(CameraAt));
	ofs.write((char*)_cameraUp, sizeof(CameraUp));

	//write global ambient light
	ofs.write((char*)_globalAmbientLight, 4*sizeof(float));

	ofs.close();
	return true;
}

//load the scene from a file
bool Scene::loadScene(const string& filename)
{
	if (filename == "")
		return false;

	//open an output file stream
	ifstream ifs;//(filename.c_str());
	ifs.open(filename.c_str());

	//get the file version
	int fileVersion = 0;
	ifs.read((char*)&fileVersion, sizeof(int));
	if(fileVersion != 1)
		return false;

	//get the primitives count
	int primitiveCount = 0;
	ifs.read((char*)&primitiveCount, sizeof(int));

	//get the lights count
	int lightCount = 0;
	ifs.read((char*)&lightCount, sizeof(int));

	//get the primitives
	for(int i = 0; i< primitiveCount; i++)
	{
		//load the type
		GEOMETRYTYPE type = GT_NONE;
		ifs.read((char*)&type, sizeof(GEOMETRYTYPE));

		//load the geometry
		Geometry* newGeometry = 0;
		switch(type)
		{
			case GT_SPHERE:
				newGeometry = new GeoSphere();
				ifs.read((char*)newGeometry, sizeof(GeoSphere));
				break;
			case GT_BOX:
				newGeometry = new GeoBox();
				ifs.read((char*)newGeometry, sizeof(GeoBox));
				break;
			case GT_CYLINDER:
				newGeometry = new GeoCylinder();
				ifs.read((char*)newGeometry, sizeof(GeoCylinder));
				break;
			case GT_TORUS:
				newGeometry = new GeoTorus();
				ifs.read((char*)newGeometry, sizeof(GeoTorus));
				break;
			case GT_MESH:
				newGeometry = new GeoMesh();
				//ifs.read((char*)newGeometry, sizeof(GeoMesh));
				loadMesh(ifs, (GeoMesh*)newGeometry);
				break;
			default :
				return false;
		}

		_primitives << newGeometry;
	}

	//get the lights
	for(int i = 0; i < lightCount; i++)
	{
		//load the type
		LIGHTTYPE type = LT_NONE;
		ifs.read((char*)&type, sizeof(LIGHTTYPE));

		//load the light itself
		GUIILight* newLight = 0;
		GUIILight* newAt = 0;
		switch(type)
		{
			case LT_DIRECTIONAL:
				//load the directional light
				newLight = new DirectionalLight(0);
				ifs.read((char*)newLight, sizeof(DirectionalLight));
				_lights << newLight;

				//load the at element
				newAt = new DirectionalAt();
				ifs.read((char*)newAt, sizeof(DirectionalAt));
				_lights << newAt;

				//create the links
				((DirectionalLight*)newLight)->setAtElement((DirectionalAt*)newAt);
				((DirectionalAt*)newAt)->setDirectionalLightElement((DirectionalLight*)newLight);
				break;

			case LT_SPOT:
				//load spot light
				newLight = new SpotLight(0);
				ifs.read((char*)newLight, sizeof(SpotLight));
				_lights << newLight;

				//load the at element
				newAt = new SpotAt();
				ifs.read((char*)newAt, sizeof(SpotAt));
				_lights << newAt;

				//create link
				((SpotLight*)newLight)->setAtElement((SpotAt*)newAt);
				((SpotAt*)newAt)->setSpotLightElement((SpotLight*)newLight);
				
				break;

			case LT_POINT:
				newLight = new PointLight();
				ifs.read((char*)newLight, sizeof(PointLight));
				_lights << newLight;
				break;

			default:
				break;
		}
	}

	//load the camera elements
	ifs.read((char*)_cameraEye, sizeof(CameraEye));
	ifs.read((char*)_cameraAt, sizeof(CameraAt));
	ifs.read((char*)_cameraUp, sizeof(CameraUp));
	_cameraEye->setAtElement(_cameraAt);
	_cameraEye->setUpElement(_cameraUp);

	//load global ambient light
	ifs.read((char*)_globalAmbientLight, sizeof(float)*4);
	
	ifs.close();
	return true;
	
}

//delete all the primitives of the scene
void Scene::deleteAllPrimitives()
{
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		delete (*i);
	}

	_primitives.clear();
}

//delete all the lights of the scene
void Scene::deleteAllLights()
{
	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		delete (*i);
	}

	_lights.clear();
}

//set the RT camera
void Scene::setRTCamera(const CVector3f& eyeElement, const CVector3f& atElement, const CVector3f& upElement)
{
	_cameraEye->setTranslate(eyeElement);
	_cameraAt->setTranslate(atElement);
	_cameraUp->setTranslate(upElement);
}

//unselect all the primitives
void Scene::unselectAll()
{
	for(QLinkedList<Geometry*>::iterator i = _primitives.begin(); i != _primitives.end(); i++)
	{
		(*i)->setSelected(false);
	}

	for(QLinkedList<GUIILight*>::iterator i = _lights.begin(); i != _lights.end(); i++)
	{
		(*i)->setSelected(false);
	}

	_cameraEye->setSelected(false);
	_cameraAt->setSelected(false);
	_cameraUp->setSelected(false);
}

//get the first selected object
Movable* Scene::getFirstSelectedObject()
{
	return _firstSelectedObject;
}

//set the first selected object
void Scene::setFirstSelectedObject(Movable* firstSelectedObject)
{
	_firstSelectedObject = firstSelectedObject;
}